package com.itheima.d8_stream;
import java.util.*;
import java.util.stream.Stream;

/**
 * 目标：掌握Stream流提供的常见中间方法。
 *      中间方法指的是调用完成后会返回新的Stream流，可以继续使用(支持链式编程)。
 *
 *          Stream提供的常用中间方法                                 说明
 *  Stream<T> filter(Predicate<? super T> predicate)            用于对流中的数据进行过滤。
 *  Stream<T>sorted()                                           对元素进行升序排序
 *  Stream<T>sorted(Comparator<? super T> comparator)           按照指定规则排序
 *  Stream<T> limit (long maxSize)                              获取前几个元素
 *  Stream<T> skip(long n)                                      跳过前几个元素
 *  Stream<T> distinct()                                        去除流中重复的元素。
 *  <R>Stream<R>map(Function<? super T,? extends R>mapper)      对元素进行加工，并返回对应的新流
 *  static <T> Stream<T> concat(Stream a, Stream b)             合并a和b两个流为一个流
 *
 *
 * 问题：我要自定义排序规则的时候，用的是double类型的怎么办，就像今天这个案例一样结果是错误的
 *      要用Double的compare方法
 */
public class StreamTest3 {
    public static void main(String[] args) {
        List<Double> scores = new ArrayList<>();
        Collections.addAll(scores, 88.5, 100.0, 60.0, 99.0, 9.5, 99.6, 25.0);
        // 需求1：找出成绩大于等于60分的数据，并升序后，再输出。
        //filter方法返回新的Stream流支持链式编程，foreach不是中间方法，而是返回一个void
        scores.stream().filter(s -> s >= 60).sorted().forEach(s -> System.out.println(s));

        List<Student> students = new ArrayList<>();
        Student s1 = new Student("蜘蛛精", 26, 172.5);
        Student s2 = new Student("蜘蛛精", 26, 172.5);
        Student s3 = new Student("紫霞", 23, 167.6);
        Student s4 = new Student("白晶晶", 25, 169.0);
        Student s5 = new Student("牛魔王", 35, 183.3);
        Student s6 = new Student("牛夫人", 34, 168.5);
        Collections.addAll(students, s1, s2, s3, s4, s5, s6);
        // 需求2：找出年龄大于等于23,且年龄小于等于30岁的学生，并按照年龄降序输出.
        System.out.println("----------并按照年龄降序输出-----------------");
        students.stream().filter(s -> s.getAge() >= 23 && s.getAge() <= 30)
                .sorted((o1, o2) -> o2.getAge() - o1.getAge())      //自定义排序规则，用lambda表达式来写
                .forEach(s -> System.out.println(s));

        System.out.println("---------自己试一试写不简化的，按照身高升序,结果是错的-----------");
        students.stream().filter(a->a.getHeight()>170).sorted(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return (int) (o1.getHeight()-o2.getHeight());
            }
        }).forEach(s-> System.out.println(s));

        System.out.println("---------自己试一试，按照身高升序,用lambda表达式写,也是错的-----------");
        students.stream().filter(a->a.getHeight()>170)
                .sorted((o1, o2) -> (int) (o1.getHeight()-o2.getHeight())).forEach(a-> System.out.println(a));

        System.out.println("-------------------要用Double的compare方法-------------");
        students.stream().filter(s->s.getHeight()>170)
                .sorted(((o1, o2) -> Double.compare(o2.getHeight(),o1.getHeight()))).forEach(s-> System.out.println(s));

        System.out.println("----------------取出身高最高的前3名学生-------------");

        // 需求3：取出身高最高的前3名学生，并输出。降序排列取前三个
        students.stream().sorted(((o1, o2) -> Double.compare(o2.getHeight(),o1.getHeight())))
                        .limit(3).forEach(s-> System.out.println(s));
        System.out.println("--简化：如果某个Lambda表达式里只是调用一个实例方法，并且前后参数的形式一致，就可以使用实例方法引用---");
        System.out.println("-简化：如果某个Lambda表达式里只是调用一个静态方法，并且前后参数的形式一致，就可以使用静态方法引用。--");
        students.stream().sorted((o1, o2) -> Double.compare(o2.getHeight(), o1.getHeight()))
                .limit(3).forEach(System.out::println);

        System.out.println("--------------------skip方法跳过前几个元素-------------------------");

        // 需求4：取出身高倒数的2名学生，并输出。   s1 s2 s3 s4 s5 s6
        students.stream().sorted((o1, o2) -> Double.compare(o2.getHeight(), o1.getHeight()))
                .skip(students.size() - 2).forEach(System.out::println);

        System.out.println("---------map和distinct去重：----------");

        // 需求5：找出身高超过168的学生叫什么名字，要求去除重复的名字，再输出。
        //map方法也是中间方法，也是会返回一个新的流，s就是遍历前面限定>168得到的流，然后把遍历到的学生对象的学生名字返回一个流
        //接着得到的流里面虽然有两个名字的字符串都是蜘蛛精，但还是需要distinct去重。不用distinct就会出现两个蜘蛛精
        students.stream().filter(s->s.getHeight()>168).map(s->s.getName())
                        .distinct().forEach(System.out::println);
        System.out.println("----------------s->s.getName()特定类型的方法简化写法：-------------------");
        //s是参数，这个参数作为主调来调用getname方法，没有入参，那么就属于特定类型的方法引用
        students.stream().filter(s -> s.getHeight() > 168).map(Student::getName)
               .distinct().forEach(System.out::println);

        System.out.println("----------distinct去重自定义类型对象元素---------------");
        //需求5plus：找出身高超过168的学生对象，把内容相同的学生对象视为重复，删除
        // distinct去重复元素，如果元素是自定义类型的对象（如果希望内容一样就认为重复，那需要重写自定义类里面的hashCode,equals）
        students.stream().filter(s -> s.getHeight() > 168)
                .distinct().forEach(System.out::println);

        System.out.println("---------------concat方法------------");
        //Stream类提供的如下方法:  public static<T> Stream<T> of(T... values)       获取当前接收数据的Stream流
        //static <T> Stream<T> concat(Stream a, Stream b)             合并a和b两个流为一个流
        Stream<String> st1 = Stream.of("张三", "李四");
        Stream<String> st2 = Stream.of("张三2", "李四2", "王五");
        Stream<String> allSt = Stream.concat(st1, st2);
        allSt.forEach(System.out::println);

        System.out.println("自己写个例子试试:   ");
        Stream<Integer> zeng11 = Stream.of(25,22);
        Stream<String> jihong = Stream.of("曾季洪","她");
        Stream<Object> family = Stream.concat(zeng11,jihong);
        family.forEach(System.out::println);

    }
}
